﻿/*
	Copyright © Bogdan Kobets 2011
	http://ElasticLogic.com
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace ElasticLogic.TagsMarker.Collections
{

	public class CommonCollection<T> : IEnumerable<T>
	{
		protected T[] Items;

		private int count;
		private int capacity;
		private readonly double increment;
		private readonly bool unique;

		public int Count
		{
			get { return count; }
		}

		public int EmptyItems
		{
			get { return capacity - count; }
		}

		public bool Unique
		{
			get { return unique; }
		}

		public CommonCollection(bool unique, int capacity, double growStep)
		{
			if (capacity < 0)
				throw new ArgumentOutOfRangeException("capacity");
			if (growStep <= 0.0)
				throw new ArgumentOutOfRangeException("growStep");

			this.capacity = capacity;
			this.increment = 1 + growStep / 100;
			this.unique = unique;

			this.count = 0;
			this.Items = new T[capacity];
		}

		public CommonCollection(bool unique)
			: this(unique, 10, 10.0)
		{
		}

		public CommonCollection()
			: this(false, 10, 10.0)
		{
		}

		public bool Contains(T item)
		{
			return Items.Contains(item);
		}

		private int IndexOf(T item)
		{
			return Array.IndexOf(Items, item);
		}

		private bool CanAddItem(T item)
		{
			if (unique)
				return !Contains(item);
			else
				return true;
		}

		protected bool AddItem(T item)
		{
			if (CanAddItem(item))
			{
				if (EmptyItems == 0)
				{
					int newCapacity = (int)(capacity * increment);
					if (newCapacity == capacity)
						++newCapacity;

					T[] copy = new T[newCapacity];
					capacity = newCapacity;

					Array.Copy(Items, copy, count);
					Items = copy;
				}

				++count;
				Items[count - 1] = item;

				return true;
			}
			else
				return false;
		}

		protected bool RemoveItem(T item)
		{
			int pos = IndexOf(item);
			if (pos >= 0)
			{
				if (pos < count - 1)
				{
					Array.Copy(Items, pos + 1, Items, pos, count - pos - 1);
				}
				--count;

				return true;
			}
			else
				return false;
		}

		public void ReserveItems(int need)
		{
			int diff = need - EmptyItems;
			if (diff > 0)
			{
				capacity += diff;
				T[] copy = new T[capacity];

				Array.Copy(Items, copy, count);
				Items = copy;
			}
		}

		public int TrimItems()
		{
			int diff = EmptyItems;

			if (diff > 0)
			{
				T[] copy = new T[count];
				Array.Copy(Items, copy, count);

				Items = copy;
				capacity = count;
			}

			return diff;
		}

		public IEnumerator<T> GetEnumerator()
		{
			for (int i = 0; i < count; i++)
			{
				yield return Items[i];
			}
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

	}

}
